package edu.hawaii.halealohacli.command;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.List;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.source.jaxb.Source;

/**
 * Monitors the power of a given tower based on a optional time interval given. Also reports wether
 * the tower is meeting its goal for energy conservation. Default time interval if none is provided
 * is 10 seconds.
 * Differs from specifications as it defaults set-baseline to 48 hours.
 * There was no easy way to implement a way to keep and retrieve the data with the
 * per command instanced nature of the base program, unless it were to create a 
 * external file.
 * 
 * Modified from MonitorPower by Michell Kupfer.
 * 
 * @author John MacMillan
 * 
 */
public class MonitorGoal implements Command {
  private String name;
  private String usage;
  private String description;
  protected WattDepotClient client;
  protected String nameFromUser;
  protected int interval;
  protected int goal;
  protected boolean exitMonitor;

  /**
   * Default constructor.
   */
  public MonitorGoal() {
    this.name = "monitor-goal";
    this.usage = "monitor-goal [tower | lounge] [goal] [interval]";
    this.description =
        "This command prints out a timestamp and the current"
            + " power for [tower | lounge] as well as if it is meeting the"
            + "energy reduction of [goal]% every [interval] seconds.";
    // this.errorMsg = "monitor-goal was incorrectly used";
    this.interval = 10000;
    this.exitMonitor = true;
  }

  /**
   * Initializes this command with a healthy WattDepot client.
   * 
   * @param client A healthy WattDepot client used to initialize the command
   * @param args * Arguments required by the command for execution
   * @return Weather or not the arguments passed was accepted by the command initialization
   */
  @Override
  public boolean initializeCommand(WattDepotClient client, String[] args) {
    // set a client
    this.client = client;

    if (args.length < 3 || args.length > 4) {
      System.out.format("The arguments are invalid " + "(Enter \"help\" to see command usage)");
      return false;
    }

    // cast and save the goal
    this.nameFromUser = args[1];
    try {
      this.goal = Integer.parseInt(args[2]);
      if (this.goal < 1 || this.goal > 99) {
        System.out.println("Invalid goal, please enter an integer between 1 and 99");
        return false;
      }
    }
    catch (Exception e) {
      System.out.println("Invalid goal, please enter an integer between 1 and 99");
      return false;
    }
    // if a interval was specific, cast it and save it.
    if (args.length == 4) {
      try {
        this.interval = 1000 * Integer.parseInt(args[3]);
      }
      catch (Exception e) {
        System.out.println("Invalid interval, please enter interval in seconds.");
        return false;
      }
    }
    getData();
    return true;
  }

  /**
   * Gets the power data from the given tower at the specified interval. Stops monitoring power in a
   * user enter any input.
   * 
   * @return If an error occurs returns false.
   */
  public boolean getData() {

    // creates a new runnable for a thread that will execute then delay.
    Runnable run = new Runnable() {
      public void run() {
        try {
          while (exitMonitor) {

            printer();
            Thread.sleep(interval);

          }
        }
        catch (InterruptedException iex) {
          exitMonitor = false;
        }
      }
    };

    Thread thread = new Thread(run);
    thread.start();

    try {
      while (System.in.available() == 0) {
        // Annoying code to avoid a PMD error.
        exitMonitor = false;
        exitMonitor = true;
      }
    }
    catch (IOException e) {
      exitMonitor = false;
      return false;
    }

    exitMonitor = false;
    return true;

  }

  /**
   * Prints out the values from each interval.
   */
  public void printer() {

    try {
      SourceData dataModule;
      long dTime;
      SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd kk:mm:ss", Locale.US);
      SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
      Source source;
      XMLGregorianCalendar timestamp;
      SetBaseline setbase = new SetBaseline();
      List<Double> baselist;
      double baseline;
      double powerGoal;
      String meetsGoal = "does";
      // create the objects needed to initialize getBaseValue.
      String[] args = new String[2];
      args[0] = "set-baseline";
      args[1] = nameFromUser;
      setbase.initializeCommand(client, args);
      source = client.getSource(nameFromUser);
      timestamp = client.getLatestSensorData(nameFromUser).getTimestamp();
      dataModule = new SourceData(source.getName());
      dataModule.setPowerConsumed(client.getPowerConsumed(source.getName(), timestamp));
      dTime = timestamp.toGregorianCalendar().getTimeInMillis();
      dataModule.setTime(format.format(dTime));
      // create the needed offset calendar, defaulting to 48 hours.
      Calendar defaultTime = GregorianCalendar.getInstance();
      defaultTime.add(Calendar.HOUR, -48);
      setbase.initializeData(nameFromUser, format2.format(defaultTime.getTime()));
      baselist = setbase.getBaselineValueForJohn();
      // retrieve the hour.
      baseline = baselist.get(Integer.parseInt(format.format(dTime).substring(12, 14)) - 1);
      // calculate the power goal.
      powerGoal = baseline * (1 - ((double) goal / 100));
      // perform the goal check
      if (!(powerGoal >= dataModule.getPowerConsumed())) {
        meetsGoal = "does not";
      }
      // print results.
      System.out.format("%s%s %s %s %.2f%s %s %s %s%s%.2f %s%n", dataModule.getName(),
          "'s power as of", dataModule.getTime(), "was", dataModule.getPowerConsumed(), ".\nThis",
          meetsGoal, "meet the goal of", goal, "% (", powerGoal, "kW).");
    }

    catch (Exception e) {
      System.out.println("Cannot connect to Tower " + nameFromUser + " : " + e);
      exitMonitor = false;
    }
  }

  /**
   * Returns the name of this command.
   * 
   * @return the name of this command
   */
  @Override
  public String getName() {
    return name;
  }

  /**
   * Returns the usage syntax of this command.
   * 
   * @return the usage syntax of this command
   */
  @Override
  public String getUsage() {
    return usage;
  }

  /**
   * Returns the description of this command.
   * 
   * @return a description of this command
   */
  @Override
  public String getDescription() {
    return description;
  }

  /**
   * Prints the results of executing this command.
   */
  public void printResults() {
    System.out.println("Monitor-Goal Has Been Exited, The Following Two Lines Can Be Ignored");
  }
}
